/*
 * EventLoop.cpp
 *
 *	Author: stravant
 *	Description:
 *		
 */

#include <algorithm>
#include "EventLoop.h"
#include "../core/AutoLock.h"
#include <boost/foreach.hpp>

namespace tbt {

EventLoop::EventLoop(): mInDestructor(false), mIsRunning(false), mStopRequested(false) {
	//sett |current()|
	assert(current().get() == nullptr && "Two event-loops created one one thread!");
	mCurrentEventLoop.set(this);
}

EventLoop::~EventLoop() {
	//unset |current()|
	mCurrentEventLoop.set(0);

	mInDestructor = true;

	//delete tasks
	BOOST_FOREACH(Task* task, mTaskList)
		delete task;
	mTaskList.clear();
	BOOST_FOREACH(Task* task, mTaskAdd)
		delete task;
	mTaskAdd.clear();

	//run destructionlisteners
	BOOST_FOREACH(DestructionListener* dest, mDestructionListeners) {
		dest->onEventLoopDestroyed();
	}
}

void EventLoop::run() {
	mStopRequested = false;
	mIsRunning = true;

	//main loop
	while (!mStopRequested) {
		/* Cache the current time */
		Time now = Time::Now();

		/**** run tasks ****/
		updateTasks();
		auto it = mTaskList.begin();
		while (it != mTaskList.end()) {
			if ((*it)->tick(now)) {
				//delete
				delete (*it);
				it = mTaskList.erase(it);
			} else {
				//increment
				++it;
			}
		}

		/*** run actions ***/
		//run queue; Queue should be empty to start, and keep running until
		//actions are no longer being added to the queue. (That is, in running
		//an action more actions may be added, and the expectation is that they
		//should be run ASAP)
		while (updateActions()) {
			while (!mActionQueue.empty()) {
				mActionQueue.front()->run();
				delete mActionQueue.front();
				mActionQueue.pop();
			}
		}

		/*** run delayedactions ***/
		updateDelayedActions(); //update

		//run applicable delayedactions which should be run at this time
		while (!mDelayedActionQueue.empty()) {
			DelayedAction* action = mDelayedActionQueue.top();
			//if the highest priority (soonest) task is not ready, break
			if (action->TimeToRun > now)
				break;

			//otherwise run it
			action->run();
			delete action;
			mDelayedActionQueue.pop();
		}
	}

	mIsRunning = false;
}

bool EventLoop::isRunning() const {
	return mIsRunning;
}

void EventLoop::stopSoon() {
	mStopRequested = true;
}

void EventLoop::addDestructionListener(DestructionListener* o) {
	//AutoLock lock(mDestructionListenerWriteLock);
	//mDestructionListenerAdd.push_back(o);
	mDestructionListeners.push_back(o);
}

void EventLoop::removeDestructionListener(DestructionListener* o) {
	//if (!mInDestructor) {
	//	AutoLock lock(mDestructionListenerWriteLock);
	//	mDestructionListenerRemove.push_back(o);
	//}
	mDestructionListeners.remove(o);
}

void EventLoop::queueAction(Action* action) {
	AutoLock lock(mActionWriteLock);
	mActionAdd.push_back(action);
}

void EventLoop::queueActionDelayed(Action* action, TimeDelta delay) {
	AutoLock lock(mDelayedActionWriteLock);
	mDelayedActionAdd.push_back(new DelayedAction(action, Time::Now() + delay));
}

void EventLoop::addTask(Task* task) {
	AutoLock lock(mTaskListWriteLock);
	mTaskAdd.push_back(task);
}

bool EventLoop::updateTasks() {
	//update task list
	AutoLock lock(mTaskListWriteLock);
	if (!mTaskAdd.empty()) {
		BOOST_FOREACH(Task* task, mTaskAdd)
			mTaskList.push_back(task);
		mTaskAdd.clear();
		return true;
	} else {
		return false;
	}
}

bool EventLoop::updateActions() {
	//update action queue
	AutoLock lock(mActionWriteLock);
	if (!mActionAdd.empty()) {
		BOOST_FOREACH(Action* action, mActionAdd)
			mActionQueue.push(action);
		mActionAdd.clear();
		return true;
	} else {
		return false;
	}
}

bool EventLoop::updateDelayedActions() {
	//update delayedaction list
	AutoLock lock(mDelayedActionWriteLock);
	if (!mDelayedActionAdd.empty()) {
		BOOST_FOREACH(DelayedAction* action, mDelayedActionAdd)
			mDelayedActionQueue.push(action);
		mDelayedActionAdd.clear();
		return true;
	} else {
		return false;
	}
}

ThreadLocalPtr<EventLoop> EventLoop::mCurrentEventLoop;

}












